/***************************************************************************
 *   Copyright (C) 2009 by Jason Pullara                                   *
 *   lordkat@gmail.com                                                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "common.h"

cLuaEngine::cLuaEngine() {
	this->m_luaState = NULL;
	this->m_vecItems.resize(5);
	this->m_vecMaps.resize(5);
	this->loadLua();
}

cLuaEngine::~cLuaEngine() {
	lua_close(this->m_luaState);
}

void cLuaEngine::loadLua() {
	cClient *pClient = NULL;
	cNetwork *pNetwork = NULL;
	string sTemp = "1";
	if (this->m_luaState != NULL) {
		pClient = luabind::object_cast<cClient *>(luabind::globals(this->m_luaState)["thisClient"]);
		pNetwork = luabind::object_cast<cNetwork *>(luabind::globals(this->m_luaState)["thisNetwork"]);
		lua_close(this->m_luaState);
		this->m_luaState = NULL;
	}
	this->m_luaState = lua_open();
	luaL_openlibs(this->m_luaState);
	luabind::open(this->m_luaState);
	luabind::module(this->m_luaState) [
		luabind::class_<cLuaEngine>("cLuaEngine")
			.def("mapDesc", &cLuaEngine::luaMapDesc)
			.def("mapExit", &cLuaEngine::luaMapExit)
			.def("registerItem", &cLuaEngine::registerItem)
			.def("registerMap", &cLuaEngine::registerMap)
			.def("listItems", &cLuaEngine::listItems)
			.def("reloadLua", &cLuaEngine::loadLua),
		luabind::class_<sHitData>("sHitData")
			.def_readwrite("hitType", &sHitData::hitType)
			.def_readwrite("damage", &sHitData::ulDamage),
		luabind::class_<sItemData>("sItemData")
			.def_readwrite("name", &sItemData::sName)
			.def_readwrite("desc", &sItemData::sDesc),
		luabind::class_<sMapData>("sMapData")
			.def_readwrite("zone", &sMapData::sZone)
			.def_readwrite("desc", &sMapData::sDesc),
		luabind::class_<sCharacterData>("sCharacterData")
			.def_readwrite("name", &sCharacterData::sName)
			.def_readwrite("password", &sCharacterData::sPassword)
			.def_readwrite("title", &sCharacterData::sTitle)
			.def_readwrite("job", &sCharacterData::ucJob)
			.def_readwrite("sub", &sCharacterData::ucSubJob)
			.def_readwrite("race", &sCharacterData::ucRace)
			.def_readwrite("jobNameLong", &sCharacterData::sJobLong)
			.def_readwrite("jobNameShort", &sCharacterData::sJobShort)
			.def_readwrite("jobRank", &sCharacterData::sJobRank)
			.def_readwrite("subNameLong", &sCharacterData::sSubLong)
			.def_readwrite("subNameShort", &sCharacterData::sSubShort)
			.def_readwrite("subRank", &sCharacterData::sSubRank)
			.def_readwrite("raceNameLong", &sCharacterData::sRaceLong)
			.def_readwrite("raceNameShort", &sCharacterData::sRaceShort)
			.def_readwrite("raceRank", &sCharacterData::sRaceRank)
			.def_readwrite("tnl", &sCharacterData::ulTNL)
			.def_readwrite("map", &sCharacterData::ulMap)
			.def_readwrite("gold", &sCharacterData::ulGold)
			.def_readwrite("HP", &sCharacterData::ulHP)
			.def_readwrite("MP", &sCharacterData::ulMP)
			.def_readwrite("TP", &sCharacterData::ulTP)
			.def_readwrite("maxHP", &sCharacterData::ulMaxHP)
			.def_readwrite("maxMP", &sCharacterData::ulMaxMP)
			.def_readwrite("maxTP", &sCharacterData::ulMaxTP)
			.def_readwrite("eqHead", &sCharacterData::ulEqHead)
			.def_readwrite("eqNeck1", &sCharacterData::ulEqNeck1)
			.def_readwrite("eqNeck2", &sCharacterData::ulEqNeck2)
			.def_readwrite("eqEar1", &sCharacterData::ulEqEar1)
			.def_readwrite("eqEar2", &sCharacterData::ulEqEar2)
			.def_readwrite("eqBack", &sCharacterData::ulEqBack)
			.def_readwrite("eqChest", &sCharacterData::ulEqChest)
			.def_readwrite("eqArms", &sCharacterData::ulEqArms)
			.def_readwrite("eqHands", &sCharacterData::ulEqHands)
			.def_readwrite("eqWaist1", &sCharacterData::ulEqWaist1)
			.def_readwrite("eqWaist2", &sCharacterData::ulEqWaist2)
			.def_readwrite("eqLegs", &sCharacterData::ulEqLegs)
			.def_readwrite("eqFeet", &sCharacterData::ulEqFeet)
			.def_readwrite("eqHold1", &sCharacterData::ulEqHold1)
			.def_readwrite("eqHold2", &sCharacterData::ulEqHold2)
			.def_readwrite("eqRangedWeapon", &sCharacterData::ulEqRangedWeapon)
			.def_readwrite("eqRangedAmmo", &sCharacterData::ulEqRangedAmmo)
			.def_readonly("str", &sCharacterData::uiStr)
			.def_readonly("dex", &sCharacterData::uiDex)
			.def_readonly("vit", &sCharacterData::uiVit)
			.def_readonly("agi", &sCharacterData::uiAgi)
			.def_readonly("int", &sCharacterData::uiInt)
			.def_readonly("mnd", &sCharacterData::uiMnd)
			.def_readonly("chr", &sCharacterData::uiChr)
			.def_readonly("atk", &sCharacterData::uiAtk)
			.def_readonly("def", &sCharacterData::uiDef)
			.def_readonly("acc", &sCharacterData::uiAcc)
			.def_readonly("eva", &sCharacterData::uiEva)
			.def_readonly("mac", &sCharacterData::uiMAc)
			.def_readonly("mev", &sCharacterData::uiMEv)
			.def_readwrite("modStr", &sCharacterData::uiStrMod)
			.def_readwrite("modDex", &sCharacterData::uiDexMod)
			.def_readwrite("modVit", &sCharacterData::uiVitMod)
			.def_readwrite("modAgi", &sCharacterData::uiAgiMod)
			.def_readwrite("modInt", &sCharacterData::uiIntMod)
			.def_readwrite("modMnd", &sCharacterData::uiMndMod)
			.def_readwrite("modChr", &sCharacterData::uiChrMod)
			.def_readwrite("modAtk", &sCharacterData::uiAtkMod)
			.def_readwrite("modDef", &sCharacterData::uiDefMod)
			.def_readwrite("modAcc", &sCharacterData::uiAccMod)
			.def_readwrite("modEva", &sCharacterData::uiEvaMod)
			.def_readwrite("modMAc", &sCharacterData::uiMAcMod)
			.def_readwrite("modMEv", &sCharacterData::uiMEvMod)
			.def_readwrite("damage", &sCharacterData::uiDamage)
			.def_readwrite("delay", &sCharacterData::uiDelay),
		luabind::class_<cCharacter>("cCharacter")
			.def_readwrite("chardata", &cCharacter::m_charData)
			.def("attack", &cCharacter::attack)
			.def("gainExp", &cCharacter::gainExp)
			.def("getEXP",	&cCharacter::getEXP)
			.def("getJobLevel", &cCharacter::getJobLevel)
			.def("getSubLevel", &cCharacter::getSubLevel)
			.def("addItem", &cCharacter::addItem)
			.def("delItem", &cCharacter::delItem),
		luabind::class_<cClient>("cClient")
			.def("sendData", &cClient::sendData)
			.def("setClientState", &cClient::setClientState),
		luabind::class_<cNetwork>("cNetwork")
			.def("sendAllOnMap", &cNetwork::sendAllOnMap)
			.def("sendPlayersOnMap", &cNetwork::sendPlayersOnMap)
			.def("findPlayer", &cNetwork::findPlayer)
	];
	luabind::globals(this->m_luaState)["thisInput"] = sTemp;
	luabind::globals(this->m_luaState)["this"] = this;
	if (pClient != NULL && pNetwork != NULL) {
		luabind::globals(this->m_luaState)["thisClient"] = pClient;
		luabind::globals(this->m_luaState)["thisCharacter"] = &pClient->m_characterData;
		luabind::globals(this->m_luaState)["thisCharacterData"] = &pClient->m_characterData.m_charData;
		luabind::globals(this->m_luaState)["thisNetwork"] = pNetwork;
	}
	if (luaL_dofile(this->m_luaState, "data/scripts/main.lua"))
		std::cout<<"LUA Error: "<<lua_tostring(this->m_luaState, -1)<<std::endl;
	luabind::call_function<void>(this->m_luaState, "collectgarbage", "collect");
	return;
}

void cLuaEngine::setData(cClient *pClient, cNetwork *pNetwork, string sInput) {
	luabind::globals(this->m_luaState)["thisClient"] = pClient;
	luabind::globals(this->m_luaState)["thisCharacter"] = &pClient->m_characterData;
	luabind::globals(this->m_luaState)["thisCharacterData"] = &pClient->m_characterData.m_charData;
	luabind::globals(this->m_luaState)["thisNetwork"] = pNetwork;
	luabind::globals(this->m_luaState)["thisInput"] = sInput;
	return;
}

void cLuaEngine::executeCommand(cClient *pClient, cNetwork *pNetwork, string sCommand, string sInput, string sParam) {
	try {
		luabind::globals(this->m_luaState)["thisClient"] = pClient;
		luabind::globals(this->m_luaState)["thisCharacter"] = &pClient->m_characterData;
		luabind::globals(this->m_luaState)["thisCharacterData"] = &pClient->m_characterData.m_charData;
		luabind::globals(this->m_luaState)["thisNetwork"] = pNetwork;
		luabind::globals(this->m_luaState)["thisInput"] = sInput;
		luabind::globals(this->m_luaState)["this"] = this;
		lua_getglobal(this->m_luaState, sCommand.c_str());
		if (lua_isfunction(this->m_luaState, lua_gettop(this->m_luaState))) {
			if (sParam.empty())
				luabind::call_function<void>(this->m_luaState, sCommand.c_str());
			else
				luabind::call_function<void>(this->m_luaState, sCommand.c_str(), sParam);
		}
		else {
			string sTemp = "\r\nI don't understand what '";
			sTemp += sCommand;
			sTemp += "' is.\r\n";
			pClient->sendData(sTemp);
		}
		luabind::call_function<void>(this->m_luaState, "collectgarbage", "collect");
	}
	catch (const luabind::error &lbe) {
		std::cout<<"LUA Error: "<<lbe.what()<<std::endl;
		lua_State *ls = lbe.state();
		std::cout<<lua_tostring(ls, -1)<<std::endl;
		luabind::call_function<void>(this->m_luaState, "collectgarbage", "collect");
		return;
	}
	return;
}

void cLuaEngine::luaMapDesc() {
	cClient *pClient = luabind::object_cast<cClient *>(luabind::globals(this->m_luaState)["thisClient"]);
	sCharacterData *pCharData = luabind::object_cast<sCharacterData *>(luabind::globals(this->m_luaState)["thisCharacterData"]);
	cNetwork *pNetwork = luabind::object_cast<cNetwork *>(luabind::globals(this->m_luaState)["thisNetwork"]);
	string sInput = luabind::object_cast<string>(luabind::globals(this->m_luaState)["thisInput"]);
	string sTemp = "\r\n\033[1;4m";
	sTemp += this->m_vecMaps[pCharData->ulMap].sZone;
	sTemp += "\033[0m\r\n";
	sTemp += this->m_vecMaps[pCharData->ulMap].sDesc;
	pClient->sendData(sTemp);
	pNetwork->sendPlayersOnMap(pClient, pCharData->ulMap);
	return;
}

void cLuaEngine::luaMapExit() {
	cClient *pClient = luabind::object_cast<cClient *>(luabind::globals(this->m_luaState)["thisClient"]);
	cNetwork *pNetwork = luabind::object_cast<cNetwork *>(luabind::globals(this->m_luaState)["thisNetwork"]);
	string sInput = luabind::object_cast<string>(luabind::globals(this->m_luaState)["thisInput"]);
	string sTemp = "map_";
	sTemp += ultoa(pClient->m_characterData.m_charData.ulMap, 10);
	this->executeCommand(pClient, pNetwork, sTemp, sInput, "exit");
	return;
}

void cLuaEngine::registerItem(unsigned long ulID, string sName, string sDesc) {
	if (ulID >= this->m_vecItems.capacity())
		this->m_vecItems.resize(this->m_vecItems.capacity() + ulID);
	this->m_vecItems[ulID].sName = sName;
	this->m_vecItems[ulID].sDesc = sDesc;
	return;
}

void cLuaEngine::registerMap(unsigned long ulID, string sZone, string sDesc) {
	if (ulID >= this->m_vecMaps.capacity())
		this->m_vecMaps.resize(this->m_vecMaps.capacity() + ulID);
	this->m_vecMaps[ulID].sZone = sZone;
	this->m_vecMaps[ulID].sDesc = sDesc;
	return;
}

void cLuaEngine::listItems() {
	bool bFound = false;
	string sTemp = "";
	cClient *pClient = luabind::object_cast<cClient *>(luabind::globals(this->m_luaState)["thisClient"]);
	sCharacterData *pData = luabind::object_cast<sCharacterData *>(luabind::globals(this->m_luaState)["thisCharacterData"]);
	for (list<sInventoryData>::iterator i = pData->lInventory.begin(); i != pData->lInventory.end(); ++i) {
		bFound = true;
		sItemData itemTemp = this->m_vecItems[(*i).ulItemID];
		sTemp += itemTemp.sName;
		sTemp += " (";
		sTemp += itoa((*i).ucNum, 10);
		sTemp += ")";
		sTemp += "\r\n";
	}
	if (!bFound)
		sTemp += "Nothing!\r\n";
	pClient->sendData(sTemp);
	return;
}

void cLuaEngine::equipItem(unsigned long ulItemID) {
	cClient *pClient = luabind::object_cast<cClient *>(luabind::globals(this->m_luaState)["thisClient"]);
	sCharacterData *pData = luabind::object_cast<sCharacterData *>(luabind::globals(this->m_luaState)["thisCharacterData"]);
	cNetwork *pNetwork = luabind::object_cast<cNetwork *>(luabind::globals(this->m_luaState)["thisNetwork"]);
	string sInput = luabind::object_cast<string>(luabind::globals(this->m_luaState)["thisInput"]);
	
	return;
}
